﻿using OpenCvSharp;
using System.ComponentModel;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Security;
using OpenCvSharp.Extensions;
using System.Drawing.Imaging;

namespace Om.Camera.Normal;

public class NormalCamera : IBaseCamera
{
    public event EventHandler<bool>? ConnectionChanged;
    private bool _isConnected;
    public bool IsConnected
    {
        get => _isConnected;
        private set
        {
            if (_isConnected != value)
            {
                _isConnected = value;
                ConnectionChanged?.Invoke(this, value);
            }
        }
    }
    public event Action<byte[]>? FrameReceived;
    public string Id { get; private set; }

    private List<string> _cameraNameList = new List<string>();
    public Task<List<string>> GetCameraList()
    {
        var res = NormalDevices.Devices.ToList();
        _cameraNameList = res;
        return Task.FromResult(res);
    }

    public async Task Connect(string cameraIpOrName)
    {
        var index = _cameraNameList.IndexOf(cameraIpOrName);
        if (index < 0)
        {
            throw new Exception("Camera not found");
        }

        if (!IsConnected)
        {
            my_VideoCapture = new VideoCapture(index);
            if (my_VideoCapture.IsOpened())
            {
                Id = cameraIpOrName;
                IsConnected = true;
            }

            Task.Factory.StartNew(() =>
            {
                Play_Video();
            });
        }

    }

    public async Task Disconnect()
    {
        my_VideoCapture.Release();
        IsConnected = false;
    }

    public async Task Capture(string fileName)
    {
        try
        {
            _currentBitmap.Save(fileName);
        }
        catch (Exception ex) { }
    }

    public bool CanSetExposure => false;
    public async Task<bool> SetAutoExposure(bool isAutoExposure)
    {
        return false;
    }

    public async Task<bool> SetExposureValue(int val)
    {
        return false;
    }

    public async Task<int> GetExposureValue()
    {
        return 0;
    }


    private Bitmap _currentBitmap;
    private  VideoCapture my_VideoCapture;//摄像头设备
    private void Play_Video()
    {
        while (IsConnected)
        {

            Mat v_mat = new Mat();
            my_VideoCapture.Read(v_mat);

            int sleepTime = (int)Math.Round(1000 / my_VideoCapture.Fps);

            Cv2.WaitKey(sleepTime);
            if (v_mat.Empty())
            {
                break;
            }

            //if (FlipY_flag) Cv2.Flip(v_mat, v_mat, OpenCvSharp.FlipMode.X); //上下翻转
            //if (FlipX_flag) Cv2.Flip(v_mat, v_mat, OpenCvSharp.FlipMode.Y); //左右翻转
            //if (Collimator_flag)  //画准星
            //{
            //    Cv2.Line(v_mat, new OpenCvSharp.Point(v_mat.Cols / 2, v_mat.Rows / 2 + 35), new OpenCvSharp.Point(v_mat.Cols / 2, v_mat.Rows / 2 + 15), new Scalar(0, 0, 255), 2, LineTypes.Link8);
            //    Cv2.Line(v_mat, new OpenCvSharp.Point(v_mat.Cols / 2, v_mat.Rows / 2 - 35), new OpenCvSharp.Point(v_mat.Cols / 2, v_mat.Rows / 2 - 15), new Scalar(0, 0, 255), 2, LineTypes.Link8);
            //    Cv2.Line(v_mat, new OpenCvSharp.Point(v_mat.Cols / 2 + 35, v_mat.Rows / 2), new OpenCvSharp.Point(v_mat.Cols / 2 + 15, v_mat.Rows / 2), new Scalar(0, 0, 255), 2, LineTypes.Link8);
            //    Cv2.Line(v_mat, new OpenCvSharp.Point(v_mat.Cols / 2 - 35, v_mat.Rows / 2), new OpenCvSharp.Point(v_mat.Cols / 2 - 15, v_mat.Rows / 2), new Scalar(0, 0, 255), 2, LineTypes.Link8);
            //}

            //if (ROI_flag)  //画ROI
            //{
            //    int V_width = v_mat.Width;
            //    int V_height = v_mat.Height;
            //    int start_x = v_mat.Width / 4;
            //    int start_y = v_mat.Height / 4;
            //    OpenCvSharp.Point strat_point = new OpenCvSharp.Point(v_mat.Width / 2, v_mat.Height / 2);
            //    Rect ROI_rect = new Rect(start_x, start_y, V_width / 2, V_height / 2);
            //    Mat srcImg = new Mat(v_mat, ROI_rect).Clone();
            //    Cv2.Rectangle(v_mat, ROI_rect, new Scalar(255, 255, 0), 2);
            //}
            
            var bitmap = BitmapConverter.ToBitmap(v_mat);
            using var ms = new MemoryStream();
            // 将 Bitmap 对象保存到 MemoryStream 中，指定图片格式
            bitmap.Save(ms, ImageFormat.Jpeg);
            // 将 MemoryStream 中的数据转换为 byte[]
            FrameReceived?.Invoke(ms.ToArray());

            _currentBitmap?.Dispose();
            _currentBitmap = bitmap;
            v_mat.Release();//释放内存
        }
    }
}

public class NormalDevices
{
    /// <summary>
    /// 枚举视频设备
    /// </summary>
    public static IEnumerable<string> Devices
    {
        get
        {
            IMoniker[] monikers = new IMoniker[5];
            var devEnum = Activator.CreateInstance(Type.GetTypeFromCLSID(SystemDeviceEnum)) as ICreateDevEnum;
            IEnumMoniker moniker;
            if (devEnum.CreateClassEnumerator(VideoInputDevice, out moniker, 0) == 0)
            {
                while (true)
                {
                    int hr = moniker.Next(1, monikers, IntPtr.Zero);
                    if (hr != 0 || monikers[0] == null)
                        break;
                    yield return GetName(monikers[0]);
                    foreach (var i in monikers)
                    {
                        if (i != null)
                            Marshal.ReleaseComObject(i);
                    }
                }
                Marshal.ReleaseComObject(moniker);
            }
            Marshal.ReleaseComObject(devEnum);
        }
    }
    /// <summary>
    /// 获取设备名称
    /// </summary>
    /// <param name="moniker"></param>
    /// <returns></returns>
    static string GetName(IMoniker moniker)
    {
        IPropertyBag property;
        object value;
        object temp = null;
        try
        {
            Guid guid = typeof(IPropertyBag).GUID;
            moniker.BindToStorage(null, null, ref guid, out temp);
            property = temp as IPropertyBag;
            int hr = property.Read("FriendlyName", out value, null);
            Marshal.ThrowExceptionForHR(hr);
            return value as string;
        }
        catch (Exception)
        {
            return null;
        }
        finally
        {
            if (temp != null)
            {
                Marshal.ReleaseComObject(temp);
            }
        }
    }
    static readonly Guid SystemDeviceEnum = new Guid(0x62BE5D10, 0x60EB, 0x11D0, 0xBD, 0x3B, 0x00, 0xA0, 0xC9, 0x11, 0xCE, 0x86);
    static readonly Guid VideoInputDevice = new Guid(0x860BB310, 0x5D01, 0x11D0, 0xBD, 0x3B, 0x00, 0xA0, 0xC9, 0x11, 0xCE, 0x86);
    [Flags]
    enum CDef
    {
        None = 0x0,
        ClassDefault = 0x1,
        BypassClassManager = 0x2,
        ClassLegacy = 0x4,
        MeritAboveDoNotUse = 0x8,
        DevmonCMGRDevice = 0x10,
        DevmonDMO = 0x20,
        DevmonPNPDevice = 0x40,
        DevmonFilter = 0x80,
        DevmonSelectiveMask = 0xF0
    }
    [ComImport]
    [SuppressUnmanagedCodeSecurity]
    [Guid("3127CA40-446E-11CE-8135-00AA004BB851")]
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    interface IErrorLog
    {
        [PreserveSig]
        int AddError([In][MarshalAs(UnmanagedType.LPWStr)] string pszPropName, [In] System.Runtime.InteropServices.ComTypes.EXCEPINFO pExcepInfo);
    }
    [ComImport]
    [Localizable(false)]
    [SuppressUnmanagedCodeSecurity]
    [Guid("55272A00-42CB-11CE-8135-00AA004BB851")]
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    interface IPropertyBag
    {
        [PreserveSig]
        int Read([In][MarshalAs(UnmanagedType.LPWStr)] string pszPropName, [MarshalAs(UnmanagedType.Struct)] out object pVar, [In] IErrorLog pErrorLog);

        [PreserveSig]
        int Write([In][MarshalAs(UnmanagedType.LPWStr)] string pszPropName, [In][MarshalAs(UnmanagedType.Struct)] ref object pVar);
    }

    [ComImport]
    [SuppressUnmanagedCodeSecurity]
    [Guid("29840822-5B84-11D0-BD3B-00A0C911CE86")]
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    interface ICreateDevEnum
    {
        [PreserveSig]
        int CreateClassEnumerator([In][MarshalAs(UnmanagedType.LPStruct)] Guid pType, out IEnumMoniker ppEnumMoniker, [In] CDef dwFlags);
    }
}
